package com.ocean.web.util.rwdata;

import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.constants.CF;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 适配netcdfAll-5.4.1.jar，读取裁剪后的GRAPES全球预报NC文件元数据
 * 目标文件格式：CMA_BEN_GRAPESGFS_GLDT_2025111300_240h_SSW_cropped.nc
 */
public class GRAPES_GrapesNcReader_5_4_1 {

    // 元数据实体类，包含原有字段 + 核心短名称字段
    public static class NcParam {
        private String ncFileName;       // NC文件名
        private String source;           // 来源
        private String type;             // 类型
        private String forecastBaseTime; // 起报时间
        private String forecastLeadTime; // 预报时效
        private String forecastFreq;     // 预报频次
        private String coverage;         // 覆盖范围（裁剪后实际区域）
        private String spatialRes;       // 空间分辨率
        private List<String> elements;   // 预报要素（完整信息：名称+层级+单位）
        private String elementsStr;      // 预报要素（中文顿号分割字符串，完整信息）
        private List<String> elementsCoreName; // 预报要素（核心短名称，用户期望的名称）
        private String elementsCoreNameStr; // 预报要素（核心短名称，分号隔开）

        // Getter和Setter
        public String getNcFileName() { return ncFileName; }
        public void setNcFileName(String ncFileName) { this.ncFileName = ncFileName; }
        public String getSource() { return source; }
        public void setSource(String source) { this.source = source; }
        public String getType() { return type; }
        public void setType(String type) { this.type = type; }
        public String getForecastBaseTime() { return forecastBaseTime; }
        public void setForecastBaseTime(String forecastBaseTime) { this.forecastBaseTime = forecastBaseTime; }
        public String getForecastLeadTime() { return forecastLeadTime; }
        public void setForecastLeadTime(String forecastLeadTime) { this.forecastLeadTime = forecastLeadTime; }
        public String getForecastFreq() { return forecastFreq; }
        public void setForecastFreq(String forecastFreq) { this.forecastFreq = forecastFreq; }
        public String getCoverage() { return coverage; }
        public void setCoverage(String coverage) { this.coverage = coverage; }
        public String getSpatialRes() { return spatialRes; }
        public void setSpatialRes(String spatialRes) { this.spatialRes = spatialRes; }
        public List<String> getElements() { return elements; }
        public void setElements(List<String> elements) { this.elements = elements; }
        public String getElementsStr() { return elementsStr; }
        public void setElementsStr(String elementsStr) { this.elementsStr = elementsStr; }
        public List<String> getElementsCoreName() { return elementsCoreName; }
        public void setElementsCoreName(List<String> elementsCoreName) { this.elementsCoreName = elementsCoreName; }
        public String getElementsCoreNameStr() { return elementsCoreNameStr; }
        public void setElementsCoreNameStr(String elementsCoreNameStr) { this.elementsCoreNameStr = elementsCoreNameStr; }

        // 格式化输出结果（第10条显示核心短名称，分号隔开）
        @Override
        public String toString() {
            return "============ GRAPES NC文件参数 ============\n" +
                    "1. NC文件名：" + ncFileName + "\n" +
                    "2. 来源：" + source + "\n" +
                    "3. 类型：" + type + "\n" +
                    "4. 起报时间：" + forecastBaseTime + "\n" +
                    "5. 预报时效：" + forecastLeadTime + "\n" +
                    "6. 预报频次：" + forecastFreq + "\n" +
                    "7. 覆盖范围（裁剪后）：" + coverage + "\n" +
                    "8. 空间分辨率：" + spatialRes + "\n" +
                    "9. 预报要素（完整信息）：" + elementsStr + "\n" +
                    "10. 预报要素（核心短名称，分号隔开）：" + elementsCoreNameStr + "\n" +
                    "===========================================";
        }
    }

    /**
     * 核心读取方法（完全适配netcdfAll-5.4.1.jar）
     * @param ncFilePath 你的NC文件完整路径
     * @return 包含10个参数的NcParam对象
     * @throws IOException 文件读取异常
     */
    public static NcParam readGrapesNcParams(String ncFilePath) throws IOException {
        NetcdfFile ncFile = null;
        NetcdfDataset ncDataset = null;
        NcParam ncParam = new NcParam();

        try {
            // 1. 打开NC文件（5.4.1版本标准打开方式）
            ncFile = NetcdfFile.open(ncFilePath);
            ncDataset = NetcdfDataset.wrap(ncFile, Collections.emptySet());
            System.out.println("✅ 文件打开成功：" + ncFilePath);

            // 2. 解析文件名（双重方案：正则匹配 + 下划线分割备用）
            parseFileNameWithFallback(ncFilePath, ncParam);

            // 3. 读取文件内置属性
            parseFileAttrs(ncDataset, ncParam);

            // 4. 解析空间信息（5.4.1兼容版）
            parseSpatialInfo_5_4_1(ncDataset, ncParam);

            // 5. 解析预报要素（完整信息 + 核心短名称）
            parseForecastElements(ncDataset, ncParam);

            return ncParam;

        } finally {
            // 安全关闭资源
            if (ncDataset != null) {
                try { ncDataset.close(); } catch (Exception e) { System.err.println("❌ Dataset关闭异常：" + e.getMessage()); }
            }
            if (ncFile != null) {
                try { ncFile.close(); } catch (Exception e) { System.err.println("❌ File关闭异常：" + e.getMessage()); }
            }
        }
    }

    /**
     * 双重保障的文件名解析（正则匹配 + 下划线分割备用）
     * 确保起报时间和预报时效100%能解析
     */
    private static void parseFileNameWithFallback(String ncFilePath, NcParam ncParam) {
        String fileName = Paths.get(ncFilePath).getFileName().toString();
        ncParam.setNcFileName(fileName);
        System.out.println("\n📄 开始解析文件名：" + fileName);

        // 方案1：超级兼容正则匹配
        boolean regexSuccess = parseByRegex(fileName, ncParam);
        if (regexSuccess) {
            return;
        }

        // 方案2：备用方案 - 按下划线分割文件名，提取关键字段
        System.out.println("\n⚠️  正则匹配失败，启用备用方案：下划线分割解析");
        boolean splitSuccess = parseBySplit(fileName, ncParam);
        if (splitSuccess) {
            return;
        }

        // 两种方案都失败
        System.err.println("❌ 所有解析方案均失败！");
        ncParam.setType("GRAPES全球大气数值预报（裁剪版）");
        ncParam.setForecastBaseTime("解析失败：无法识别文件名格式");
        ncParam.setForecastLeadTime("解析失败：无法识别文件名格式");
    }

    /**
     * 方案1：超级兼容的正则匹配
     * 支持：
     * - 起报时间10位（yyyyMMddHH）
     * - 时效2-3位数字+h（24h/240h）
     * - 任意位置的_cropped后缀
     * - 中间字段的微小变化（如GLDT可能为其他字符）
     */
    private static boolean parseByRegex(String fileName, NcParam ncParam) {
        String regex = "CMA_BEN_.*?_GLDT_(\\d{10})_(\\d{2,3}h)_.*?(cropped)?.*?\\.nc";
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(fileName);

        if (matcher.find()) {
            System.out.println("✅ 正则匹配成功（超级兼容模式）");
            System.out.println("   - 起报时间字符串：" + matcher.group(1));
            System.out.println("   - 预报时效：" + matcher.group(2));
            System.out.println("   - 是否裁剪：" + (matcher.group(3) != null ? "是" : "否"));

            // 解析类型
            String type = "GRAPESGFS 全球大气数值预报";
            if (matcher.group(3) != null) type += "（裁剪版）";
            ncParam.setType(type);

            // 解析起报时间
            String timeStr = matcher.group(1);
            try {
                LocalDateTime baseTime = LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern("yyyyMMddHH"));
                String formattedTime = baseTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                ncParam.setForecastBaseTime(formattedTime);
                System.out.println("✅ 起报时间解析成功：" + formattedTime);
            } catch (DateTimeParseException e) {
                System.err.println("❌ 起报时间格式错误：" + timeStr);
                return false;
            }

            // 解析预报时效
            ncParam.setForecastLeadTime(matcher.group(2));
            System.out.println("✅ 预报时效解析成功：" + matcher.group(2));
            return true;
        } else {
            System.err.println("❌ 超级兼容正则匹配失败");
            return false;
        }
    }

    /**
     * 方案2：备用方案 - 按下划线分割
     * 逻辑：
     * 1. 按下划线分割文件名（去掉后缀）
     * 2. 查找10位数字的起报时间
     * 3. 查找"数字+h"格式的预报时效
     */
    private static boolean parseBySplit(String fileName, NcParam ncParam) {
        // 去掉文件后缀（.nc）
        String fileNameWithoutExt = fileName.replaceAll("\\.nc$", "").toLowerCase();
        // 按下划线分割
        String[] parts = fileNameWithoutExt.split("_");

        String baseTimeStr = null;
        String leadTimeStr = null;
        boolean isCropped = fileNameWithoutExt.contains("cropped");

        // 遍历分割后的部分，查找关键字段
        for (String part : parts) {
            // 查找10位数字（起报时间：yyyyMMddHH）
            if (part.matches("\\d{10}")) {
                baseTimeStr = part;
            }
            // 查找2-3位数字+h（预报时效）
            else if (part.matches("\\d{2,3}h")) {
                leadTimeStr = part;
            }
        }

        // 检查是否找到关键字段
        if (baseTimeStr != null && leadTimeStr != null) {
            System.out.println("✅ 下划线分割解析成功");
            System.out.println("   - 起报时间字符串：" + baseTimeStr);
            System.out.println("   - 预报时效：" + leadTimeStr);
            System.out.println("   - 是否裁剪：" + (isCropped ? "是" : "否"));

            // 解析类型
            String type = "GRAPESGFS 全球大气数值预报";
            if (isCropped) type += "（裁剪版）";
            ncParam.setType(type);

            // 解析起报时间
            try {
                LocalDateTime baseTime = LocalDateTime.parse(baseTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHH"));
                String formattedTime = baseTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                ncParam.setForecastBaseTime(formattedTime);
                System.out.println("✅ 起报时间解析成功：" + formattedTime);
            } catch (DateTimeParseException e) {
                System.err.println("❌ 起报时间格式错误：" + baseTimeStr);
                return false;
            }

            // 解析预报时效
            ncParam.setForecastLeadTime(leadTimeStr);
            System.out.println("✅ 预报时效解析成功：" + leadTimeStr);
            return true;
        } else {
            System.err.println("❌ 下划线分割解析失败：未找到关键字段");
            System.err.println("   分割后的字段：" + String.join(", ", parts));
            return false;
        }
    }

    /**
     * 解析文件内置属性
     */
    private static void parseFileAttrs(NetcdfDataset ncDataset, NcParam ncParam) {
        String source = "中国气象局（CMA）GRAPES全球预报模式（wgrib2创建 + CDO合并 + 裁剪处理）";
        ncParam.setSource(source);

        // 预报频次提取
        String freq = "3小时/次（默认）";
        String history = ncDataset.findGlobalAttribute("history") != null ?
                ncDataset.findGlobalAttribute("history").getStringValue() : "";

        Pattern hourPattern = Pattern.compile("GRAPES_GFS(\\d{3})\\.nc");
        Matcher hourMatcher = hourPattern.matcher(history);
        List<Integer> hours = new ArrayList<>();
        while (hourMatcher.find()) {
            hours.add(Integer.parseInt(hourMatcher.group(1)));
        }

        if (hours.size() >= 2) {
            Collections.sort(hours);
            int interval = hours.get(1) - hours.get(0);
            freq = interval + "小时/次（自动提取）";
        }
        ncParam.setForecastFreq(freq);
    }

    /**
     * 5.4.1兼容版空间信息解析（关键修复）
     * 完全使用5.4.1原生API，避免版本兼容问题
     */
    private static void parseSpatialInfo_5_4_1(NetcdfDataset ncDataset, NcParam ncParam) {
        List<Double> lonList = new ArrayList<>();
        List<Double> latList = new ArrayList<>();

        System.out.println("\n🔍 开始遍历所有变量，查找经纬度（5.4.1兼容模式）...");
        for (Variable var : ncDataset.getVariables()) {
            String varName = var.getShortName().toLowerCase();
            String units = var.findAttribute("units") != null ? var.findAttribute("units").getStringValue().toLowerCase() : "";
            String stdName = var.findAttribute(CF.STANDARD_NAME) != null ? var.findAttribute(CF.STANDARD_NAME).getStringValue().toLowerCase() : "";

            // 打印变量信息，方便调试
            System.out.println("检查变量：" + var.getShortName() +
                    " | 维度：" + var.getDimensions() +
                    " | 单位：" + units +
                    " | 标准名：" + stdName +
                    " | 数据类型：" + var.getDataType());

            // 三重匹配：确保不遗漏经纬度变量
            boolean isLongitude = varName.contains("lon") || units.contains("degrees_east") || stdName.contains("longitude");
            boolean isLatitude = varName.contains("lat") || units.contains("degrees_north") || stdName.contains("latitude");

            if (isLongitude || isLatitude) {
                try {
                    // 🔥 5.4.1原生方法：read() → copyTo1DJavaArray()
                    Object dataObj = var.read().copyTo1DJavaArray();
                    int dataLength = dataObj instanceof Object[] ? ((Object[]) dataObj).length : 0;
                    System.out.println("✅ 匹配到经/纬度变量：" + var.getShortName() + "，数据长度：" + dataLength);

                    // 解析数据为Double列表（兼容所有数值类型）
                    List<Double> dataList = parseData_5_4_1(dataObj);
                    if (dataList != null && !dataList.isEmpty()) {
                        if (isLongitude) {
                            lonList.addAll(dataList);
                            System.out.println("📌 经度数据读取成功，共" + lonList.size() + "个点");
                        } else {
                            latList.addAll(dataList);
                            System.out.println("📌 纬度数据读取成功，共" + latList.size() + "个点");
                        }
                    } else {
                        System.err.println("❌ 变量" + var.getShortName() + "数据解析为空");
                    }
                } catch (Exception e) {
                    System.err.println("❌ 读取变量" + var.getShortName() + "异常：" + e.getMessage());
                    e.printStackTrace();
                }
            }
        }

        // 1. 计算覆盖范围
        String coverage = "裁剪区域（经纬度数据为空）";
        if (!lonList.isEmpty() && !latList.isEmpty()) {
            try {
                double minLon = Collections.min(lonList);
                double maxLon = Collections.max(lonList);
                double minLat = Collections.min(latList);
                double maxLat = Collections.max(latList);

                // 格式化输出（处理南纬、东经范围）
                coverage = String.format("北纬%.2f°-南纬%.2f° / 东经%.2f°-%.2f°",
                        maxLat, Math.abs(minLat), minLon, maxLon);
                System.out.println("\n🌍 覆盖范围计算成功：" + coverage);
            } catch (Exception e) {
                System.err.println("❌ 覆盖范围计算异常：" + e.getMessage());
                coverage = "裁剪区域（计算异常）";
            }
        }
        ncParam.setCoverage(coverage);

        // 2. 计算空间分辨率
        String spatialRes = "0.50°×0.50°（默认）";
        if (!lonList.isEmpty() && lonList.size() > 1 && !latList.isEmpty() && latList.size() > 1) {
            try {
                double lonRes = calculateResolution(lonList);
                double latRes = calculateResolution(latList);

                // 校验分辨率合理性（气象数据常见范围：0.01°~5°）
                if (lonRes > 0.01 && lonRes < 5 && latRes > 0.01 && latRes < 5) {
                    spatialRes = String.format("%.2f°×%.2f°（实测）", latRes, lonRes);
                    System.out.println("📏 分辨率计算成功：" + spatialRes);
                } else {
                    System.out.println("⚠️  分辨率异常（lonRes：" + lonRes + "，latRes：" + latRes + "），使用默认值");
                }
            } catch (Exception e) {
                System.err.println("❌ 分辨率计算异常：" + e.getMessage());
            }
        }
        ncParam.setSpatialRes(spatialRes);
    }

    /**
     * 5.4.1兼容版数据解析（处理所有数值类型）
     * 避免强制类型转换，安全解析为Double列表
     */
    private static List<Double> parseData_5_4_1(Object dataObj) {
        List<Double> dataList = new ArrayList<>();
        if (dataObj == null) {
            System.err.println("❌ 数据对象为空");
            return null;
        }

        // 按数据类型分类解析（5.4.1支持的数值类型）
        if (dataObj instanceof int[]) {
            for (int val : (int[]) dataObj) dataList.add((double) val);
        } else if (dataObj instanceof short[]) {
            for (short val : (short[]) dataObj) dataList.add((double) val);
        } else if (dataObj instanceof long[]) {
            for (long val : (long[]) dataObj) dataList.add((double) val);
        } else if (dataObj instanceof float[]) {
            for (float val : (float[]) dataObj) dataList.add((double) val);
        } else if (dataObj instanceof double[]) {
            for (double val : (double[]) dataObj) dataList.add(val);
        } else if (dataObj instanceof Number[]) {
            for (Number val : (Number[]) dataObj) dataList.add(val.doubleValue());
        } else {
            System.err.println("❌ 不支持的数据类型：" + dataObj.getClass().getName());
            return null;
        }

        // 打印前3个数据，验证解析结果
        if (dataList.size() > 0) {
            System.out.println("📊 数据解析示例：" + dataList.subList(0, Math.min(3, dataList.size())));
        }
        return dataList;
    }

    /**
     * 计算分辨率（相邻点平均间隔）
     */
    private static double calculateResolution(List<Double> dataList) {
        Collections.sort(dataList);
        double totalDiff = 0;
        int count = 0;

        for (int i = 1; i < dataList.size(); i++) {
            double diff = Math.abs(dataList.get(i) - dataList.get(i - 1));
            // 过滤异常间隔（避免0或过大的值）
            if (diff > 0.001 && diff < 10) {
                totalDiff += diff;
                count++;
            }
        }

        return count > 0 ? totalDiff / count : 0.5; // 默认0.5°
    }

    /**
     * 解析预报要素（核心修改：提取核心短名称，去掉下划线后的层级信息）
     */
    private static void parseForecastElements(NetcdfDataset ncDataset, NcParam ncParam) {
        List<String> elements = new ArrayList<>(); // 完整信息（名称+层级+单位）
        List<String> elementsCoreName = new ArrayList<>(); // 核心短名称（用户期望的名称）
        System.out.println("\n📋 开始解析预报要素...");

        for (Variable var : ncDataset.getVariables()) {
            // 排除经纬度、时间变量
            String varName = var.getShortName().toLowerCase();
            if (varName.contains("lon") || varName.contains("lat") || varName.contains("time")) {
                continue;
            }

            // 排除坐标变量
            if (var.isCoordinateVariable()) {
                continue;
            }

            // 1. 构建完整要素信息（保留原有逻辑）
            String elemFullName = var.findAttribute(CF.LONG_NAME) != null ?
                    var.findAttribute(CF.LONG_NAME).getStringValue() : var.getShortName();
            if (var.findAttribute("level") != null) {
                elemFullName += " [" + var.findAttribute("level").getStringValue() + "]";
            }
            if (var.findAttribute("units") != null) {
                elemFullName += "（单位：" + var.findAttribute("units").getStringValue() + "）";
            }
            elements.add(elemFullName);
            System.out.println("✅ 要素（完整信息）：" + elemFullName);

            // 2. 提取核心短名称（核心修改：去掉下划线后的层级信息）
            String originalShortName = var.getShortName();
            String coreName = originalShortName;
            // 按下划线分割，取第一部分（适配 "要素名_层级" 格式）
            if (originalShortName.contains("_")) {
                coreName = originalShortName.split("_")[0];
            }
            // 二次校验：如果分割后为空，保留原短名称
            coreName = coreName.trim().isEmpty() ? originalShortName : coreName;
            elementsCoreName.add(coreName);
            System.out.println("✅ 要素（原始短名称）：" + originalShortName + " → （核心短名称）：" + coreName);
        }

        // 3. 生成字符串格式
        String elementsStr = String.join("、", elements); // 完整信息：中文顿号隔开
        String elementsCoreNameStr = String.join(";", elementsCoreName); // 核心短名称：分号隔开

        // 赋值到实体类
        ncParam.setElements(elements);
        ncParam.setElementsStr(elementsStr);
        ncParam.setElementsCoreName(elementsCoreName);
        ncParam.setElementsCoreNameStr(elementsCoreNameStr);

        System.out.println("\n📝 预报要素（完整信息-顿号分割）：" + elementsStr);
        System.out.println("📝 预报要素（核心短名称-分号分割）：" + elementsCoreNameStr);
    }

    // 测试入口
    public static void main(String[] args) {
        // 替换为你的实际文件路径（Windows用\\转义）
        String ncFilePath = "E:\\nc\\GRAPES\\CMA_BEN_GRAPESGFS_GLDT_2025111800_240h_SSW_calculate.nc";

        try {
            NcParam result = readGrapesNcParams(ncFilePath);
            System.out.println("\n" + result);
        } catch (IOException e) {
            System.err.println("❌ 文件读取失败：" + e.getMessage());
            e.printStackTrace();
        }
    }
}